package com.easygoods.shiro.realm;

import com.bestvike.stone.core.lang.Convert;
import com.bestvike.stone.core.lang.DigestUtils;
import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.spring.shiro.auth.Auth;
import com.bestvike.stone.spring.shiro.authc.AuthenticationToken;
import com.bestvike.stone.spring.shiro.authc.exception.AuthenticationException;
import com.bestvike.stone.spring.shiro.authc.exception.account.DisabledAccountException;
import com.bestvike.stone.spring.shiro.authc.exception.account.LockedAccountException;
import com.bestvike.stone.spring.shiro.authc.exception.account.UnknownAccountException;
import com.bestvike.stone.spring.shiro.authc.exception.credentials.IncorrectCredentialsException;
import com.easygoods.dao.MemberInfoDao;
import com.easygoods.dao.OrderInfoDao;
import com.easygoods.dao.StoreInfoDao;
import com.easygoods.dao.StoreSpuDao;
import com.easygoods.entity.MemberInfo;
import com.easygoods.entity.StoreInfo;
import com.easygoods.enums.LoginResult;
import com.easygoods.enums.LoginUserType;
import com.easygoods.enums.UserState;
import com.easygoods.shiro.auth.UserAuth;
import com.easygoods.shiro.token.UserToken;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import tk.mybatis.mapper.entity.Example;

/**
 * Created by 许崇雷 on 2019-03-12.
 */
@CommonsLog
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
@Component
public final class UserRealm extends AbstractRealm {
    @Autowired
    private MemberInfoDao memberInfoDao;
    @Autowired
    private StoreInfoDao storeInfoDao;
    @Autowired
    private OrderInfoDao orderInfoDao;
    @Autowired
    private StoreSpuDao storeSpuDao;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UserToken;
    }

    @Override
    public Auth authenticate(AuthenticationToken token) throws AuthenticationException {
        //用户名密码登录
        final UserToken userToken = (UserToken) token;
        final Example example = new Example(MemberInfo.class);
        example.createCriteria().andEqualTo(MemberInfo.MOBILE, userToken.getUsername());
        final MemberInfo memberInfo = this.memberInfoDao.selectOneByExample(example);
        if (memberInfo == null) {
            this.writeLoginLog(null, userToken.getUsername(), null, null, LoginResult.NotExists);
            throw new UnknownAccountException();
        }

        //状态
        UserState userState = Convert.toType(memberInfo.getState(), UserState.class);
        if (userState != UserState.Normal) {
            this.writeLoginLog(memberInfo.getId(), memberInfo.getMobile(), null, null, userState.toLoginResult());
            throw new LockedAccountException();
        }

        //密码错误
        String encryptPwd = this.encryptPassword(memberInfo.getId(), userToken.getPassword(), memberInfo.getPwdSalt());
        if (!StringUtils.equals(memberInfo.getPwd(), encryptPwd)) {
            this.writeLoginLog(null, userToken.getUsername(), null, null, LoginResult.PwdError);
            throw new IncorrectCredentialsException();
        }

        //查询该用户店铺
        final Example storeExample = new Example(StoreInfo.class);
        storeExample.createCriteria().andEqualTo(StoreInfo.MEMBER_ID, memberInfo.getId());
        final StoreInfo storeInfo = this.storeInfoDao.selectOneByExample(storeExample);
        final String storeId = storeInfo == null ? null : storeInfo.getId();
        if (storeInfo == null || StringUtils.isBlank(storeId)) {
            log.error(String.format("用户 '%s' 没有 store 信息", memberInfo.getId()));
            this.writeLoginLog(memberInfo.getId(), memberInfo.getMobile(), null, null, userState.toLoginResult());
            throw new DisabledAccountException();
        }

        //查询店铺收藏,是否创建过订单
        final boolean isStoreSpuExist = this.storeSpuDao.isStoreSpuExist(storeId);
        final boolean isOrderInfoExist = this.orderInfoDao.isOrderInfoExist(memberInfo.getId());

        //redis 记录 token
        final UserAuth userAuth = UserAuth.loginSuccess(null, null, memberInfo, isStoreSpuExist || isOrderInfoExist ? LoginUserType.OldUser : LoginUserType.NewUser);
        //返回
        this.writeLoginLog(memberInfo.getId(), memberInfo.getMobile(), null, null, LoginResult.Success);
        return userAuth;
    }

    //密码加密
    public static String encryptPassword(String uid, String password, String salt) {
        Assert.hasText(uid, "uid can not be empty");
        Assert.hasText(password, "password can not be empty");
        Assert.hasText(salt, "salt can not be emtpy");

        return DigestUtils.md5Hex(uid + password + salt);
    }
}
